//=========================================================
// File			- vec2.h
// Author		- Jay Brown, Jesse Lampert
// Created	- 05/31/06
// Purpose	- Extention of CVector2d.h to overload more
//						operators.
//=========================================================

#ifndef __VEC2_H
#define __VEC2_H

//=== Include =============================================
#include "CMatrix2d.h"
#include "macros.h"
#include "f32.h"

//=== Classes, Structures and Unions ======================
//=========================================================
// Struct  - vec2
// Purpose - 2 dimensional vector class
//=========================================================
struct vec2
{
	f32 x;
	f32 y;

	vec2( void ) { ; }
	vec2( f32 degrees );
	vec2( const f32 * xy );
	vec2( f32 x, f32 y );
	vec2( const vec2 & v );
	vec2( const CVector2d & v );
	~vec2( void ){ ; }

	f32 Length( void ) const;
	f32 LengthSq( void ) const;

	vec2 & Clear( );
	vec2 & Set( f32 x, f32 y );
	vec2 & Set( const vec2 & v );
	vec2 & Normalize( );
	vec2 & ProjectOnto( const vec2 & v );
	vec2 & LerpTo( const vec2 & v, f32 u );
	vec2 & Rotate( f32 degrees );

	f32 Dot( const vec2 & v ) const;

	f32 Angle( void ) const;
	f32 Angle( const vec2 & v ) const;
	f32 AngleTo( const vec2 & v ) const;

	vec2 & operator=( f32 degrees );
	vec2 & operator=( const f32 * xy );

	vec2 & operator+=( const vec2 & v );
	vec2 & operator+=( f32 val );
	vec2 & operator-=( const vec2 & v );
	vec2 & operator-=( f32 val );
	vec2 & operator*=( f32 val );
	vec2 & operator*=( const CMatrix2d & a );
	vec2 & operator/=( f32 val );

	f32 & operator[]( uint32 idx );
	f32 operator[]( uint32 idx ) const;

	friend vec2 operator+( const vec2 & v1, const vec2 & v2 );
	friend vec2 operator+( const vec2 & v, f32 val );
	friend vec2 operator+( f32 val, const vec2 & v );
	friend vec2 operator-( const vec2 & v1, const vec2 & v2 );
	friend vec2 operator-( const vec2 & v, f32 val );
	friend vec2 operator-( f32 val, const vec2 & v );
	friend f32 operator*( const vec2 & v1, const vec2 & v2 );
	friend vec2 operator*( const vec2 & v, f32 val );
	friend vec2 operator*( f32 val, const vec2 & v );
	friend vec2 operator*( const CMatrix2d & a, const vec2 & x );
	friend vec2 operator*( const vec2 & x, const CMatrix2d & a );
	friend vec2 operator/( const vec2 & v, f32 val );
	friend vec2 operator/( f32 val, const vec2 & v );
	friend boolean operator==( const vec2 & v1, const vec2 & v2 );
	friend boolean operator!=( const vec2 & v1, const vec2 & v2 );
};

//=== Methods =============================================
//=========================================================
// Function - vec2::vec2
// Author   - Jay Brown
//=========================================================
inline vec2::vec2( f32 degrees )
{
	x = CMathFixed::Cos( degrees.Value );
	y = CMathFixed::Sin( degrees.Value );
}

//=========================================================
// Function - vec2::vec2
// Author   - Jay Brown
//=========================================================
inline vec2::vec2( const f32 * xy )
{
	x = xy[0];
	y = xy[1];
}

//=========================================================
// Function - vec2::vec2
// Author   - Jay Brown
//=========================================================
inline vec2::vec2( f32 x, f32 y )
{
	this->x = x;
	this->y = y;
}

//=========================================================
// Function - vec2::vec2
// Author   - Jay Brown
//=========================================================
inline vec2::vec2( const vec2 & v )
{
	x = v.x;
	y = v.y;
}

//=========================================================
// Function - vec2::vec2
// Author   - Jay Brown
//=========================================================
inline vec2::vec2( const CVector2d & v )
{
	x = v.m_i;
	y = v.m_j;
}

//=========================================================
// Function - f32 vec2::Length
// Author   - Jay Brown
//=========================================================
inline f32 vec2::Length( void ) const
{
	return (f32)CMathFixed::Sqrt( ( x * x + y * y ).Value );
}

//=========================================================
// Function - f32 vec2::LengthSq
// Author   - Jesse Lampert
//=========================================================
inline f32 vec2::LengthSq( void ) const
{
	return x * x + y * y;
}

//=========================================================
// Function - vec2 & vec2::Clear
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::Clear( void )
{
	x = 0;
	y = 0;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::Set
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::Set( f32 x, f32 y )
{
	this->x = x;
	this->y = y;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::Set
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::Set( const vec2 & v )
{
	x = v.x;
	y = v.y;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::Normalize
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::Normalize( void )
{
	f32 len = Length( );

	if( len.Value )
		 *this /= len;

	if( x < FIXED_POINT( -1.f ) )
		x = FIXED_POINT( -1.f );
	else if( x > FIXED_POINT( 1.f ) )
		x = FIXED_POINT( 1.f );
	
	if( y < FIXED_POINT( -1.f ) )
		y = FIXED_POINT( -1.f );
	else if( y > FIXED_POINT( 1.f ) )
		y = FIXED_POINT( 1.f );

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator=( f32 degrees )
{
	x = CMathFixed::Cos( degrees );
	y = CMathFixed::Sin( degrees );

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator=( const f32 * xy )
{
	x = xy[0];
	y = xy[1];

	return *this;
}

//=========================================================
// Function - boolean operator==
// Author   - Jay Brown
//=========================================================
inline boolean operator==( const vec2 & v1, const vec2 & v2 )
{
	if( v1.x == v2.x && v1.y == v2.y )
		return TRUE;

	return FALSE;
}

//=========================================================
// Function - boolean operator!=
// Author   - Jay Brown
//=========================================================
inline boolean operator!=( const vec2 & v1, const vec2 & v2 )
{
	if( v1.x != v2.x || v1.y != v2.y )
		return TRUE;

	return FALSE;
}

//=========================================================
// Function - vec2 & vec2::operator+=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator+=( const vec2 & v )
{
	x += v.x;
	y += v.y;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator+=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator+=( f32 val )
{
	x += val;
	y += val;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator-=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator-=( const vec2 & v )
{
	x -= v.x;
	y -= v.y;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator-=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator-=( f32 val )
{
	x -= val;
	y -= val;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator*=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator*=( f32 val )
{
	x *= val;
	y *= val;

	return *this;
}

//=========================================================
// Function - vec2 & vec2::operator/=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator/=( f32 val )
{
	x /= val;
	y /= val;

	return *this;
}

//=========================================================
// Function - f32 & vec2::operator[]
// Author   - Jay Brown
//=========================================================
inline f32 & vec2::operator[]( uint32 idx )
{
	ASSERT( idx < 2 );

	return *( ( (f32 *)this ) + idx );
}

//=========================================================
// Function - f32 vec2::operator[]
// Author   - Jay Brown
//=========================================================
inline f32 vec2::operator[]( uint32 idx ) const
{
	ASSERT( idx < 2 );

	return *( ( (f32 *)this ) + idx );
}

//=========================================================
// Function - vec2 operator+
// Author   - Jay Brown
//=========================================================
inline vec2 operator+( const vec2 & v1, const vec2 & v2 )
{
	return vec2( v1.x + v2.x, v1.y + v2.y );
}

//=========================================================
// Function - vec2 operator+
// Author   - Jay Brown
//=========================================================
inline vec2 operator+( const vec2 & v, f32 val )
{
	return vec2( v.x + val, v.y + val );
}

//=========================================================
// Function - vec2 operator+
// Author   - Jay Brown
//=========================================================
inline vec2 operator+( f32 val, const vec2 & v )
{
	return vec2( val + v.x, val + v.y );
}

//=========================================================
// Function - vec2 operator-
// Author   - Jay Brown
//=========================================================
inline vec2 operator-( const vec2 & v1, const vec2 & v2 )
{
	return vec2( v1.x - v2.x, v1.y - v2.y );
}

//=========================================================
// Function - vec2 operator-
// Author   - Jay Brown
//=========================================================
inline vec2 operator-( const vec2 & v, f32 val )
{
	return vec2( v.x - val, v.y - val );
}

//=========================================================
// Function - vec2 operator-
// Author   - Jay Brown
//=========================================================
inline vec2 operator-( f32 val, const vec2 & v )
{
	return vec2( val - v.x, val - v.y );
}

//=========================================================
// Function - f32 operator* 
// Author   - Jay Brown
//=========================================================
inline f32 operator*( const vec2 & v1, const vec2 & v2 )
{
	return v1.x * v2.x + v1.y * v2.y;
}

//=========================================================
// Function - vec2 operator* 
// Author   - Jay Brown
//=========================================================
inline vec2 operator*( const vec2 & v, f32 val )
{
	return vec2( v.x * val, v.y * val );
}

//=========================================================
// Function - vec2 operator* 
// Author   - Jay Brown
//=========================================================
inline vec2 operator*( f32 val, const vec2 & v )
{
	return vec2( val * v.x, val * v.y );
}

//=========================================================
// Function - vec2 operator/
// Author   - Jay Brown
//=========================================================
inline vec2 operator/( const vec2 & v, f32 val )
{
	return vec2( v.x / val, v.y / val );
}

//=========================================================
// Function - vec2 operator/
// Author   - Jay Brown
//=========================================================
inline vec2 operator/( f32 val, const vec2 & v )
{
	return vec2( val / v.x, val / v.y );
}

//=========================================================
// Function - vec2 & vec2::ProjectOnto
// Author   - Jesse Lampert
//=========================================================
inline vec2 & vec2::ProjectOnto( const vec2 & v )
{
	f32 dot = Dot( v );
	f32 vDot = v.Dot( v );

	x = ( dot / vDot ) * v.x;
	y = ( dot / vDot ) * v.y;
	
	return *this;
}

//=========================================================
// Function - vec2 & vec2::LerpTo
// Author   - Jesse Lampert
//=========================================================
inline vec2 & vec2::LerpTo( const vec2 & v, f32 u )
{
	if( *this != v )
	{
		vec2 delta = v - *this;
		
		// snap to our position
		if( delta.LengthSq( ) <= u * u )
			Set( v );
		else
		{
			// lerp
			delta.Normalize( );
			delta *= u;

			*this += delta;
		}
	}

	return *this;
}

//=========================================================
// Function - vec2 & vec2::Rotate
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::Rotate( f32 degrees )
{
	if( degrees )
	{
		f32 sin = (f32)CMathFixed::Sin( degrees );
		f32 cos = (f32)CMathFixed::Cos( degrees );

		f32 i = x;

		x = ( cos * i ) + ( -sin * y );
		y = ( sin * i ) + ( cos * y );
	}

	return *this;
}

//=========================================================
// Function - f32 vec2::Dot
// Author   - Jesse Lampert
//=========================================================
inline f32 vec2::Dot( const vec2 & v ) const
{
	return x * v.x + y * v.y;
}

//=========================================================
// Function - f32 vec2::Angle
// Author   - Jay Brown
//=========================================================
inline f32 vec2::Angle( void ) const
{
	vec2 n( *this );

	n.Normalize( );
	
	f32 ang = (f32)CMathFixed::ACos( n.x );

	if( y < 0 && ang )
		ang = CMathFixed::Degrees_360 - ang;

	return ang;
}

//=========================================================
// Function - f32 vec2::Angle
// Author   - Jay Brown
//=========================================================
inline f32 vec2::Angle( const vec2 & v ) const
{
	f32 length = Length( ) * v.Length( );

	if( length )
		return (f32)CMathFixed::ACos( ( *this * v ) / length );
	else return (f32)0;
}

//=========================================================
// Function - f32 vec2::AngleTo
// Author   - Jay Brown
//=========================================================
inline f32 vec2::AngleTo( const vec2 & v ) const
{
	f32 a1 = Angle( );
	f32 a2 = v.Angle( );
	f32 aDiff = a2 - a1;

	if( aDiff <= -CMathFixed::Degrees_180 )
		aDiff += (f32)CMathFixed::Degrees_360;
	else if( aDiff >= CMathFixed::Degrees_180 )
		aDiff -= (f32)CMathFixed::Degrees_360;

	return aDiff;
}

//=========================================================
// Function - vec2 & vec2::operator*=
// Author   - Jay Brown
//=========================================================
inline vec2 & vec2::operator*=( const CMatrix2d & a )
{
	f32 i = x;

	x = a[0] * i + a[2] * y;
	y = a[1] * i + a[3] * y;

	return *this;
}

//=========================================================
// Function - vec2 operator* 
// Author   - Jay Brown
//=========================================================
inline vec2 operator*( const CMatrix2d & a, const vec2 & x )
{
	return vec2( a[0] * x.x + a[2] * x.y,
		a[1] * x.x + a[3] * x.y );
}

//=========================================================
// Function - vec2 operator* 
// Author   - Jay Brown
//=========================================================
inline vec2 operator*( const vec2 & x, const CMatrix2d & a )
{
	return vec2( a[0] * x.x + a[2] * x.y,
		a[1] * x.x + a[3] * x.y );
}

#endif // __VEC2_H_